﻿using System;
using System.Collections.Generic;
using System.Text;
using Windows.Foundation;
using Windows.Globalization.DateTimeFormatting;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using System.Reflection;
using System.Linq;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Controls;
using System.Runtime.Serialization;
using System.Globalization;
using Windows.Globalization;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace JiveMessenger
{
    public static class ExtensionMethods
    {

        public static void Animate(this DependencyObject target, double from, double to,
                  string propertyPath, int duration, int startTime,
                  EasingFunctionBase easing = null, Action completed = null)
        {
            Animate(target, from, to, propertyPath, duration, startTime, FillBehavior.HoldEnd, easing, completed);
        }

        public static void Animate(this DependencyObject target, double from, double to,
                          string propertyPath, int duration, int startTime, FillBehavior fillBehavior,
                          EasingFunctionBase easing = null, Action completed = null)
        {
            if (easing == null)
            {
                easing = new SineEase();
            }

            var db = new DoubleAnimation();
            db.EnableDependentAnimation = true;
            db.To = to;
            db.From = from;
            db.EasingFunction = easing;
            db.Duration = TimeSpan.FromMilliseconds(duration);
            Storyboard.SetTarget(db, target);
            Storyboard.SetTargetProperty(db, propertyPath);

            var sb = new Storyboard();
            sb.BeginTime = TimeSpan.FromMilliseconds(startTime);

            if (completed != null)
            {
                sb.Completed += (s, e) => completed();
            }
            sb.FillBehavior = fillBehavior;
            sb.Children.Add(db);

            sb.Begin();

        }



        public static void SetHorizontalOffset(this FrameworkElement fe, double offset)
        {
            var trans = new TranslateTransform()
            {
                X = offset
            };
            fe.RenderTransform = trans;

            fe.Tag = new Offset()
            {
                Value = offset,
                Transform = trans
            };
        }

        public static Offset GetHorizontalOffset(this FrameworkElement fe)
        {
            return fe.Tag == null ? new Offset() : (Offset)fe.Tag;
        }

        public struct Offset
        {
            public double Value { get; set; }
            public TranslateTransform Transform { get; set; }
        }



        public static void EnqueueWithCapacity<T>(this Queue<T> q, T item, int MaxSize)
        {
            if (q.Count >= MaxSize) q.Dequeue();
            q.Enqueue(item);
        }

        public static void AddRange<TK, TS>(this Dictionary<TK, TS> source, Dictionary<TK, TS> collection)
        {
            if (collection == null)
            {
                throw new ArgumentNullException("collection");
            }

            foreach (var item in collection)
            {
                if (!source.ContainsKey(item.Key))
                {
                    source.Add(item.Key, item.Value);
                }
            }
        }

        public static List<int> AllIndexesOf(this string str, string value)
        {
            if (String.IsNullOrEmpty(value))
                throw new ArgumentException("the string to find may not be empty", "value");
            List<int> indexes = new List<int>();
            for (int index = 0; ; index += value.Length)
            {
                index = str.IndexOf(value, index);
                if (index == -1)
                    return indexes;
                indexes.Add(index);
            }
        }

        public static void Sort<T>(this ObservableCollection<T> observable) where T : IComparable<T>, IEquatable<T>
        {
            List<T> sorted = observable.OrderBy(x => x).ToList();

            int ptr = 0;
            while (ptr < sorted.Count)
            {
                if (!observable[ptr].Equals(sorted[ptr]))
                {
                    T t = observable[ptr];
                    observable.RemoveAt(ptr);
                    observable.Insert(sorted.IndexOf(t), t);
                }
                else
                {
                    ptr++;
                }
            }
        }

        public static string TrimStart(this string source, string value)
        {
            if (!source.StartsWith(value))
                return source;

            return source.Remove(source.IndexOf(value, StringComparison.Ordinal), value.Length);
        }
        public static bool InvokeRequired(this FrameworkElement element)
        {
            return !element.Dispatcher.HasThreadAccess;
        }

        private static Type[] m_SimpleTypes = new Type[] { 
                typeof(String),
                typeof(Decimal),
                typeof(DateTime),
                typeof(DateTimeOffset),
                typeof(TimeSpan),
                typeof(Guid)
            };

        public static bool IsSimpleType(this Type type)
        {
            return
                type.GetTypeInfo().IsValueType ||
                type.GetTypeInfo().IsPrimitive ||
                m_SimpleTypes.Contains(type);
        }
    }




    public static class TimeSpanExtensions
    {
        public static string ToFormattedString(this TimeSpan span)
        {
            //TimeSpan t = TimeSpan.FromSeconds(secs);
            string answer;
            if (span.TotalMinutes < 1.0)
            {
                answer = String.Format("{0}s", span.Seconds);
            }
            else if (span.TotalHours < 1.0)
            {
                answer = String.Format("{0}m{1:D2}s", span.Minutes, span.Seconds);
            }
            else // more than 1 hour
            {
                answer = String.Format("{0}h{1:D2}m{2:D2}s", (int)span.TotalHours, span.Minutes, span.Seconds);
            }

            return answer;
        }
    }
    public static class DateTimeExtensions
    {
        public static string ToShortDateString(this DateTime dt)
        {
            GeographicRegion userRegion = new GeographicRegion();
            var userDateFormat = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("shortdate", new[] { userRegion.Code });
            return userDateFormat.Format(dt);
        }
        public static string ToShortTimeString(this DateTime dt)
        {
            //DateTimeFormatter dtf = new DateTimeFormatter("shorttime");

            //return dtf.Format(dt);
            GeographicRegion userRegion = new GeographicRegion();
            var userDateFormat = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("shorttime", new[] { userRegion.Code });
            return userDateFormat.Format(dt);
        }
    }

    public static class VisualTreeHelperExtensions
    {
        public static T GetFirstDescendantOfType<T>(this DependencyObject start) where T : DependencyObject
        {
            return start.GetDescendantsOfType<T>().FirstOrDefault();
        }

        public static IEnumerable<T> GetDescendantsOfType<T>(this DependencyObject start) where T : DependencyObject
        {
            return start.GetDescendants().OfType<T>();
        }

        public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject start)
        {
            var queue = new Queue<DependencyObject>();
            var count = VisualTreeHelper.GetChildrenCount(start);

            for (int i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(start, i);
                yield return child;
                queue.Enqueue(child);
            }

            while (queue.Count > 0)
            {
                var parent = queue.Dequeue();
                var count2 = VisualTreeHelper.GetChildrenCount(parent);

                for (int i = 0; i < count2; i++)
                {
                    var child = VisualTreeHelper.GetChild(parent, i);
                    yield return child;
                    queue.Enqueue(child);
                }
            }
        }

        public static T GetFirstAncestorOfType<T>(this DependencyObject start) where T : DependencyObject
        {
            return start.GetAncestorsOfType<T>().FirstOrDefault();
        }

        public static IEnumerable<T> GetAncestorsOfType<T>(this DependencyObject start) where T : DependencyObject
        {
            return start.GetAncestors().OfType<T>();
        }

        public static IEnumerable<DependencyObject> GetAncestors(this DependencyObject start)
        {
            var parent = VisualTreeHelper.GetParent(start);

            while (parent != null)
            {
                yield return parent;
                parent = VisualTreeHelper.GetParent(parent);
            }
        }

        public static bool IsInVisualTree(this DependencyObject dob)
        {
            return Window.Current.Content != null && dob.GetAncestors().Contains(Window.Current.Content);
        }

        public static Rect GetBoundingRect(this FrameworkElement dob, FrameworkElement relativeTo = null)
        {
            if (relativeTo == null)
            {
                relativeTo = Window.Current.Content as FrameworkElement;
            }

            if (relativeTo == null)
            {
                throw new InvalidOperationException("Element not in visual tree.");
            }

            if (dob == relativeTo)
                return new Rect(0, 0, relativeTo.ActualWidth, relativeTo.ActualHeight);

            var ancestors = dob.GetAncestors().ToArray();

            if (!ancestors.Contains(relativeTo))
            {
                throw new InvalidOperationException("Element not in visual tree.");
            }

            var pos =
                dob
                    .TransformToVisual(relativeTo)
                    .TransformPoint(new Point());
            var pos2 =
                dob
                    .TransformToVisual(relativeTo)
                    .TransformPoint(
                        new Point(
                            dob.ActualWidth,
                            dob.ActualHeight));

            return new Rect(pos, pos2);
        }
    }
}
